package mskruch.gps.experimental;


import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import mskruch.gps.Point;

/**
 * 
 * @author Slawomir.Przednowek
 * 
 * Total ascent/descent calculator based on consequent elevation changes
 * 
 */
public class ConsequentChangeCalculator {

	private static final int MINIMAL_NUMBER_OF_SUCCESSIVE_CHANGE = 6;
	private static final int MAXIMUM_TIME_BETWEEN_CHANGES = 10;

    private List<Point> pointList = null;

    public void setPoints(Point[] points) {
        pointList = Arrays.asList(points);
    }
    
    public ConsequentChangeCalculator(Point[] points) {
        setPoints(points);
    }

	private class ElevationChangesHolder {
		private double ascent = 0;
		private double descent = 0;
	}

	private ElevationChangesHolder calculateElevationChanges() {
		ElevationChangesHolder elevChanges = new ElevationChangesHolder();
		List<Point> extractedPointList = pointList;

		Point previousPoint = null;
		double ascSum = 0, descSum = 0;
		double previousElevation = 0;
		int asc = 0, desc = 0;
		for (Iterator<Point> iterator = extractedPointList.iterator(); iterator.hasNext();) {
			Point currentPoint = (Point) iterator.next();
			if (previousPoint == null) {
				previousPoint = currentPoint;
				previousElevation = currentPoint.getElevation();
			} else {

				if (currentPoint.getElevation() - previousElevation > 0) {
					asc++;
					ascSum += currentPoint.getElevation();
					desc = 0;
					descSum = 0;
				} else {
					asc = 0;
					ascSum = 0;
					desc++;
					descSum += currentPoint.getElevation();
				}
				if (asc > 0 && (asc >= MINIMAL_NUMBER_OF_SUCCESSIVE_CHANGE
						|| currentPoint.getTime() - previousPoint.getTime() > MAXIMUM_TIME_BETWEEN_CHANGES*1000)) {
					elevChanges.ascent += ((ascSum / (double) asc) - previousElevation);
					previousPoint = currentPoint;
					previousElevation = ascSum / (double) asc;
					ascSum = 0;
					asc = 0;

				} else if (desc > 0
						&& (desc >= MINIMAL_NUMBER_OF_SUCCESSIVE_CHANGE || currentPoint.getTime()
								- previousPoint.getTime() > MAXIMUM_TIME_BETWEEN_CHANGES*1000)) {
					elevChanges.descent += -descSum / (double) desc + previousElevation;

					previousPoint = currentPoint;
					previousElevation = descSum / (double) desc;
					descSum = 0;
					asc = 0;
					desc = 0;
				}
			}
		}
		return elevChanges;
	}

    public Double getElevationGain() {
        return calculateElevationChanges().ascent;
    }

    public Double getElevationLoss() {
        return calculateElevationChanges().descent;
    }

}
